Object.prototype.keys = function () {
    var keys = [];
    for (i in this) if (this.hasOwnProperty(i)) keys.push(i);
    return keys;
}

var misc = {
    line_colors : ["#ff0000", "#00bb00", "#0000ff", "#dd7700", "#0077ee", "#cc00cc", "#eebb00", "#333333", "#00bbff", "#993333"],

    contains : function(_array, _key) {
        for (var i = 0; i < _array.length; ++i)
            if (_array[i] == _key) return true;
        return false;
    },

    seegeFontStyleDef : function() {
        this.fsPLAIN = 'font-weight:normal;';
        this.fsBOLD = 'font-weight:bold;';
        this.fsITALIC = 'font-style:italic;';
        this.fsITALIC_BOLD = this.fsITALIC + this.fsBOLD;
        this.fsBOLD_ITALIC = this.fsITALIC_BOLD;
    },

    seegeFontDef : function(self, family, size, style) {
        this.family = family || 'verdana,geneva,helvetica,sans-serif';
        this.size = size || '12px';
        this.style = style || (new self.seegeFontStyleDef()).fsPLAIN;

        this.setFamily = function(new_family) { this.family = new_family; };
        this.setSize = function(new_size) { this.size = new_size; };
        this.setStyle = function(new_style) { this.style = new_style; };
    },

    seegeBrushDef : function(color) {
        this.color = color;

        this.setColor = function(new_color) { this.color = new_color; };
    },

    seegePenDef : function(size, color) {
        this.size = size;
        this.color = color;

        this.setSize = function(new_size) { this.size = new_size; };
        this.setColor = function(new_color) { this.color = new_color; };
    },

    seegeRGBAColorDef : function(r, g, b, a) {
        this.crop = function(val, min_limit, max_limit) {
            return (min_limit > max_limit) ? val : (val <= min_limit) ? min_limit : (val >= max_limit) ? max_limit : val; 
        }

        this.r = r || 0; this.g = g || 0; this.b = b || 0; this.a = a || 1.0;

        this.toString = function() {
            return (this.a >= 1.0) ? "rgb("+[this.r,this.g,this.b].join(",")+")" : "rgba("+[this.r,this.g,this.b,this.a].join(",")+")";
        };
        this.toOldString = function() {
            return '#' + this.r.toString(16) + this.g.toString(16) + this.b.toString(16);
        };

        this.scale = function(rm, gm, bm, am) {
            this.r *= rm; this.g *= gm; this.b *= bm; this.a *= am;
            return this.cropToLimits();
        };

        this.adjust = function(rd, gd, bd, ad) {
            this.r += rm; this.g += gm; this.b += bm; this.a += am;
            return this.cropToLimits();
        };

        this.cropToLimits = function() {
            this.r = this.crop(parseInt(this.r), 0, 255);
            this.g = this.crop(parseInt(this.g), 0, 255);
            this.b = this.crop(parseInt(this.b), 0, 255);
            this.a = this.crop(this.a, 0, 1);
            return this;
        };

        this.copy = function(factory) {
            return factory.getColorDef(this.r, this.g, this.b, this.a);
        };

        this.fromString = function(str) {
            var result;

            if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str)) {
                this.r = parseInt(result[1], 10);
                this.g = parseInt(result[2], 10);
                this.b = parseInt(result[3], 10);
                this.a = 1.0;
            } else if (result = /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str)) {
                this.r = parseInt(result[1], 10);
                this.g = parseInt(result[2], 10);
                this.b = parseInt(result[3], 10);
                this.a = parseFloat(result[4]);
            } else if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str)) {
                this.r = parseFloat(result[1])*2.55;
                this.g = parseFloat(result[2])*2.55;
                this.b = parseFloat(result[3])*2.55;
                this.a = 1.0;
            } else if (result = /rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str)) {
                this.r = parseFloat(result[1])*2.55;
                this.g = parseFloat(result[2])*2.55;
                this.b = parseFloat(result[3])*2.55;
                this.a = parseFloat(result[4]);
            } else if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str)) {
                this.r = parseInt(result[1], 16);
                this.g = parseInt(result[2], 16);
                this.b = parseInt(result[3], 16);
                this.a = 1.0;
            } else if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str)) {
                this.r = parseInt(result[1] + result[1], 16);
                this.g = parseInt(result[2] + result[2], 16);
                this.b = parseInt(result[3] + result[3], 16);
                this.a = 1.0;
            } else {
                this.r = this.g = this.b = 0; this.a = 1.0;
            }
            return this.cropToLimits();
        };

        this.cropToLimits();
    },

    seegeBrowserDef : function() {
        this.BrowserDetect = function() {
            var body = document.body || null;
            var tmp;
            if (body && typeof body.appendChild != 'undefined'
                        && typeof document.createRange != 'undefined'
                        && typeof (tmp = document.createRange()).setStartBefore != 'undefined'
                        && typeof tmp.createContextualFragment != 'undefined') {
                return 'browser_dom';
            } else if (body && typeof body.insertAdjacentHTML != 'undefined'
                        && typeof document.createElement != 'undefined') {
                return 'browser_ie';
            }
            return 'browser_unsupported';
        }
        if (typeof this.browser_type == 'undefined') {
            this.browser_type = this.BrowserDetect();

            this.browser_dom = 0;
            this.browser_ie = 0;
            this.browser_supported = 0;
            if (this.browser_type == 'browser_dom') {
                this.browser_dom = 1;
                this.browser_ie = 0;
                this.browser_supported = 1;
            } else if (this.browser_type == 'browser_ie') {
                this.browser_dom = 0;
                this.browser_ie = 1;
                this.browser_supported = 1;
            }
        }
    },

    seegePrintTextElement : function(container, text, browser) {
        var seegePrintTextDomElement = function(container, text) {
            var range = document.createRange();
            range.setStartBefore(container);
            var elt = range.createContextualFragment(text);
            container.appendChild(elt);
            return elt;
        }

        var seegePrintTextIEElement = function(container, text) {
            return container.insertAdjacentHTML("BeforeEnd", text);
        }

        if (browser.browser_dom) {
            return seegePrintTextDomElement(container, text);
        } else if (browser.browser_ie) {
            return seegePrintTextIEElement(container, text);
        }
        throw browser.browser_type;
    },

    seegeCreateCanvas : function(container, style, browser) {
        var seegeCreateDomCanvas = function(container, style) {
            var canvas = document.createElement('canvas');
            canvas.setAttribute('width', container.style.width);
            canvas.setAttribute('height', container.style.height);
            if (style != '') {
                canvas.setAttribute('style', style);
            }
            container.appendChild(canvas);
            return canvas.getContext("2d");
        }

        var seegeCreateIECanvas = function(container, style) {
            var canvas = document.createElement('canvas');
            canvas.setAttribute('width', container.style.width);
            canvas.setAttribute('height', container.style.height);
            if (style != '') {
                canvas.setAttribute('style', style);
            }
            container.appendChild(canvas);
            canvas = window.G_vmlCanvasManager.initElement(canvas);
            return canvas.getContext("2d");
        }

        if (browser.browser_dom) {
            return seegeCreateDomCanvas(container, style);
        } else if (browser.browser_ie) {
            return seegeCreateIECanvas(container, style);
        }
        throw browser.browser_type;
    },

    seegeCreateDiv : function(container, width, height, style) {
        var new_div = document.createElement('div');
        new_div.setAttribute('width', width);
        new_div.setAttribute('height', height);
        new_div.setAttribute('style', style);
        container.appendChild(new_div);
        return new_div;
    },

    seegeMouseAdapterDef : function(container, handler) {
        this.getOffset = function(obj) {
            var curtop = 0, curleft = 0;
            if(obj.offsetParent) while(1) {
                                                curleft += obj.offsetLeft; curtop += obj.offsetTop;
                                                if(!obj.offsetParent) break;
                                                obj = obj.offsetParent;
                                          }
            else {
                if(obj.x) curleft += obj.x;
                if(obj.y) curtop += obj.y;
            }
            return { X:curleft, Y:curtop };
        }

        var onMouseBinder = function(caller, sink) {
            return function(e) {
                var evt = e || document.event;
                var mouseX, mouseY;
                if (evt.pageX == null && evt.clientX != null) {
                    var de = document.documentElement, b = document.body;
                    mouseX = evt.clientX + (de && de.scrollLeft || b.scrollLeft || 0);
                    mouseY = evt.clientY + (de && de.scrollTop || b.scrollTop || 0);
                }
                else {
                    mouseX = evt.pageX;
                    mouseY = evt.pageY;
                }

                var offset = caller.getOffset(container);
                var localX = mouseX - offset.X;
                var localY = mouseY - offset.Y;

                sink( handler, { X:localX, Y:localY }, evt);
            }
        }

        if (handler.onMouseMoveHandler) container.onmousemove = onMouseBinder(this, handler.onMouseMoveHandler);
        if (handler.onMouseOverHandler) container.onmouseover = onMouseBinder(this, handler.onMouseOverHandler);
        if (handler.onMouseOutHandler) container.onmouseout = onMouseBinder(this, handler.onMouseOutHandler);
        if (handler.onMouseClickHandler) container.onclick = onMouseBinder(this, handler.onMouseClickHandler);
    },

};

var config = {
    getGraphTraits : function(style) {
        this.seegeDetailedCandleTraits = function() {
            this.candles_amount = 50;
            this.points_per_candle = 10;
            this.bar_width = 7;
            this.quantity_line_size = 1;
            this.filtered_line_size = 2;
            this.filtered_dot_size = 3;
            this.points_x_shift = 0.5;
            this.candle_line_size = 1.75;
            this.candle_tail_width = 3.5;
            this.candle_tail_shift = 1.5;
            this.candle_bar_shift = 1;
            this.grid_delta = 10;
            this.data_delta = 0;
        }

        this.seegeLargeCandleTraits = function() {
            this.candles_amount = 62;
            this.points_per_candle = 8;
            this.bar_width = 5;
            this.quantity_line_size = 0.75;
            this.filtered_line_size = 1.5;
            this.filtered_dot_size = 1.75;
            this.points_x_shift = 1;
            this.candle_line_size = 1.5;
            this.candle_tail_width = 2;
            this.candle_tail_shift = 0;
            this.candle_bar_shift = 0;
            this.grid_delta = 5;
            this.data_delta = 5;
        }

        this.seegeMedianCandleTraits = function() {
            this.candles_amount = 100;
            this.points_per_candle = 5;
            this.bar_width = 3;
            this.quantity_line_size = 0.5;
            this.filtered_line_size = 1;
            this.filtered_dot_size = 1.25;
            this.points_x_shift = 0.5;
            this.candle_line_size = 1;
            this.candle_tail_width = 1.5;
            this.candle_tail_shift = 0;
            this.candle_bar_shift = 0;
            this.grid_delta = 5;
            this.data_delta = 5;
        }

        this.seegeSmallCandleTraits = function() {
            this.candles_amount = 125;
            this.points_per_candle = 4;
            this.bar_width = 2;
            this.quantity_line_size = 0.5;
            this.filtered_line_size = 1;
            this.filtered_dot_size = 1;
            this.points_x_shift = 0.5;
            this.candle_line_size = 1;
            this.candle_tail_width = 1;
            this.candle_tail_shift = 0;
            this.candle_bar_shift = 0;
            this.grid_delta = 5;
            this.data_delta = 5;
        }

        this.seegeMicroCandleTraits = function() {
            this.candles_amount = 250;
            this.points_per_candle = 2;
            this.bar_width = 1;
            this.quantity_line_size = 0.75;
            this.filtered_line_size = 0.75;
            this.filtered_dot_size = 0.75;
            this.points_x_shift = 0.5;
            this.candle_line_size = 0.5;
            this.candle_tail_width = 1;
            this.candle_tail_shift = 0;
            this.candle_bar_shift = 0;
            this.grid_delta = 5;
            this.data_delta = 5;
        }

        this.seegeNanoCandleTraits = function() {
            this.candles_amount = 500;
            this.points_per_candle = 1;
            this.bar_width = 0.5;
            this.quantity_line_size = 0.5;
            this.filtered_line_size = 0.75;
            this.filtered_dot_size = 0.5;
            this.points_x_shift = 0.5;
            this.candle_line_size = 0.5;
            this.candle_tail_width = 1;
            this.candle_tail_shift = 0;
            this.candle_bar_shift = 0;
            this.grid_delta = 5;
            this.data_delta = 5;
        }

        if (style == 'detailed')
            return new this.seegeDetailedCandleTraits();
        else if (style == 'large')
            return new this.seegeLargeCandleTraits();
        else if (style == 'median')
            return new this.seegeMedianCandleTraits();
        else if (style == 'long')
            return new this.seegeSmallCandleTraits();
        else if (style == 'micro')
            return new this.seegeMicroCandleTraits();
        else if (style == 'nano')
            return new this.seegeNanoCandleTraits();
        return new this.seegeDetailedCandleTraits();
    },

    constructChartStyle : function(data, type) {
        var data_fields_count = data.data_fields_count || data.data.keys().length;
        var filtered_fields_count = data.filtered_fields_count || data.filtered.keys().length;
        var size = data_fields_count > 0 ? data.data[data.data.keys()[0]].length
                                         : filtered_fields_count > 0 ? data.filtered[data.filtered.keys()[0]].length
                                         : 0;

        if (typeof type != 'undefined') {
            if (type == 'nano')
                return [size, 500, 'nano'];
            else if (type == 'micro')
                return [size, 250, 'micro'];
            else if (type == 'long')
                return [size, 125, 'long'];
            else if (type == 'median')
                return [size, 100, 'median'];
            else if (type == 'large')
                return [size, 62, 'large'];
            else if (type == 'detailed')
                return [size, 50, 'detailed'];
            
            return [size, 50, 'detailed'];
        }

        if (size >= 450)
            return [size, 500, 'nano'];
        else if (size >= 200)
            return [size, 250, 'micro'];
        else if (size >= 186)
            return [size, 100, 'median'];
        else if (size >= 150)
            return [size, 62, 'large'];
        else if (size >= 125)
            return [size, 50, 'detailed'];
        else if (size >= 100)
            return [size, 125, 'long'];
        else if (size >= 62)
            return [size, 100, 'median'];
        else if (size >= 50)
            return [size, 62, 'large'];
        
        return [size, 50, 'detailed'];
    },

    makeDataSlice : function(slice_n, glob_data, size) {
        var data = new Object();

        data.info = glob_data.info + " #" + (slice_n + 1).toString();

        data.data_fields = (typeof glob_data.data_fields != 'undefined') ? glob_data.data_fields : glob_data.data.keys();

        if (typeof glob_data.data_field_count != 'undefined') 
            data.data_fields_count = glob_data.data_fields_count;
        else if (typeof data.data_fields != 'undefined')
             data.data_fields_count = data.data_fields.length;
        else data.data_fields_count = 0;

        data.data = new Object();

        data.filtered_fields = (typeof glob_data.filtered_fields != 'undefined') ? glob_data.filtered_fields : glob_data.filtered.keys();

        if (typeof glob_data.filtered_field_count != 'undefined') 
            data.filtered_fields_count = glob_data.filtered_fields_count;
        else if (typeof data.filtered_fields != 'undefined')
             data.filtered_fields_count = data.filtered_fields.length;
        else data.filtered_fields_count = 0;

        data.filtered = new Object();
        for (var k1 = 0; k1 < data.data_fields_count; ++k1)
        {
            if (slice_n == 0) {
                glob_data.data[data.data_fields[k1]].reverse();
            }
            data.data[data.data_fields[k1]] = glob_data.data[data.data_fields[k1]].slice(slice_n*size, (slice_n + 1)*size);
        }
        for (var k2 = 0; k2 < data.filtered_fields_count; ++k2)
        {
            if (slice_n == 0) {
                glob_data.filtered[data.filtered_fields[k2]].reverse();
            }
            data.filtered[data.filtered_fields[k2]] = glob_data.filtered[data.filtered_fields[k2]].slice(slice_n*size, (slice_n + 1)*size);
        }
        data.visible = glob_data.visible;
        data.options = glob_data.options;

        return data;
    },

};

function seegeCandleChart(container, slice_n, glob_data, style, misc, config) {
    this.misc = misc;
    this.config = config;
    this.contains = this.misc.contains;

    this.getFontStyleDef = function() {
        return new this.misc.seegeFontStyleDef();
    }
    this.seegeFontStyle = this.getFontStyleDef();

    this.getFontDef = function(family, size, style) {
        return new this.misc.seegeFontDef(this.misc, family, size, style);
    }

    this.getBrushDef = function(color) {
        return new this.misc.seegeBrushDef(color);
    }

    this.getPenDef = function(size, color) {
        return new this.misc.seegePenDef(size, color);
    }

    this.getColorDef = function(r,g,b,a) {
        return new this.misc.seegeRGBAColorDef(r,g,b,a);
    }

    this.getBrowserDef = function() {
        return new this.misc.seegeBrowserDef();
    }

    this.browser = this.getBrowserDef();

    this.mGraphTraits = this.config.getGraphTraits(style);

    this.mBlackColor = this.getColorDef(0,0,0);
    this.mWhiteColor = this.getColorDef(255,255,255);
    this.mTransparentColor = this.getColorDef(255,255,255,0.001);
    this.mSelectionColor = this.getColorDef(92,0,0,0.3);
    this.mDarkSelectionColor = this.getColorDef(64,0,0,0.5);

    this.seegeGraph = function(container, host) {
        var browser = host.getBrowserDef();

        this.mFont = host.getFontDef();
        this.mBrush = host.getBrushDef(host.getColorDef(255,255,255,1.0));
        this.mPen = host.getPenDef(1, host.getColorDef(0,0,0,1.0));
        this.mOuterContainer = container;
        this.mTextualCanvas = host.misc.seegeCreateDiv(this.mOuterContainer, this.mOuterContainer.style.width, 0, 'width:'+this.mOuterContainer.style.width+';height=0;font-size:0');
        this.mGraphicCanvas = host.misc.seegeCreateCanvas(this.mOuterContainer, '', browser);
        this.mOverlayBrush = host.getBrushDef(host.getColorDef(255,255,255,0.5));
        this.mOverlayPen = host.getPenDef(1, host.getColorDef(0,0,0,1.0));
        this.mOverlayCanvas = host.misc.seegeCreateCanvas(this.mOuterContainer, 'position:absolute;left:0px;top:0px;', browser);
        this.mPopupCanvas = host.misc.seegeCreateDiv(this.mOuterContainer, 0, 0, 'position:absolute;left:0px;top:0px;background-color:rgba(64,32,32,0.7);font-size:8px;font-weight:bold;color:#ffffff;padding:4px;display:none');

        this.printHTML = function(text) { host.misc.seegePrintTextElement(this.mTextualCanvas, text, browser); }

        this.drawString = function(str, x, y) {
            var html_buffer = '<div style="position:absolute;white-space:nowrap;'+
                'left:' + x + 'px;'+
                'top:' + y + 'px;'+
                'font-family:' +  this.mFont.family + ';'+
                'font-size:' + this.mFont.size + ';'+
                'color:' + this.mPen.color.toString() + ';' + this.mFont.style + '">'+
                str.toString() + '<\/div>';
            this.printHTML(html_buffer);
        };

        this.drawLine = function(xstart, ystart, xend, yend, where) {
            var curCanvas;
            var curPen;
            var curBrush;

            if ((typeof where == 'undefined') || where != 'overlay' ) {
                curCanvas = this.mGraphicCanvas;
                curPen = this.mPen;
                curBrush = this.mBrush;
            } else {
                curCanvas = this.mOverlayCanvas;
                curPen = this.mOverlayPen;
                curBrush = this.mOverlayBrush;
            }
            curCanvas.lineWidth = curPen.size;
            curCanvas.strokeStyle = curPen.color.toString();
            curCanvas.beginPath();
            curCanvas.moveTo(xstart, ystart);
            curCanvas.lineTo(xend, yend);
            curCanvas.stroke();
        }
        this.drawText = function(str, x, y) {
            throw 'temporary unsupported';
            this.mGraphicCanvas.font = this.mFont.size + 'px ' + this.mFont.family;
            this.mGraphicCanvas.fillStyle = this.mPen.color.toString();
            this.mGraphicCanvas.fillText(str, x, y);
        }
        this.drawRect = function(x, y, width, height, where) {
            var curCanvas;
            var curPen;
            var curBrush;

            if ((typeof where == 'undefined') || where != 'overlay' ) {
                curCanvas = this.mGraphicCanvas;
                curPen = this.mPen;
                curBrush = this.mBrush;
            } else {
                curCanvas = this.mOverlayCanvas;
                curPen = this.mOverlayPen;
                curBrush = this.mOverlayBrush;
            }

            curCanvas.lineWidth = 0.1;
            curCanvas.strokeStyle = curPen.color.toString();
            curCanvas.lineJoin = 'round';
            curCanvas.fillStyle = curBrush.color.toString();

            curCanvas.fillRect(x, y, width, height);
            curCanvas.lineWidth = curPen.size;
            curCanvas.strokeRect(x, y, width, height);
        }
        this.clearRect = function(x, y, width, height, where) {
            var curCanvas;
            var curPen;
            var curBrush;

            if ((typeof where == 'undefined') || where != 'overlay' ) {
                curCanvas = this.mGraphicCanvas;
                curPen = this.mPen;
                curBrush = this.mBrush;
            } else {
                curCanvas = this.mOverlayCanvas;
                curPen = this.mOverlayPen;
                curBrush = this.mOverlayBrush;
            }
            curCanvas.clearRect(x, y, width, height);
        }
    }

    this.mGraph = new this.seegeGraph(container, this);
    this.data = config.makeDataSlice(slice_n, glob_data, this.mGraphTraits.candles_amount);

    this.drawInfo = function(info, x, y) {
        this.mGraph.mPen.setColor(this.getColorDef(0,0,0));
        var oldFont = this.mGraph.mFont;
        this.mGraph.mFont = this.getFontDef(null, '18px', this.seegeFontStyle.fsBOLD);
        this.mGraph.drawString(info, x, y);
        this.mGraph.mFont = oldFont;
    }

    this.drawGrid = function(offset_x, offset_y, width, height) {
        this.mGraph.mPen.setSize(1);
        this.mGraph.mPen.setColor(this.getColorDef(0,0,0,1.0));
        for (var i=0; i < width/20; ++i)
            this.mGraph.drawLine(0.5 + width - i*20 - this.mGraphTraits.grid_delta + offset_x, 0 + offset_y, 0.5 + width - i*20 - this.mGraphTraits.grid_delta + offset_x, height + offset_y);
        for (var i=0; i < height/20; ++i)
            this.mGraph.drawLine(0 + offset_x, 0.5 + height/2 - (i-Math.floor(height/40))*20 + offset_y, width + offset_x, 0.5 + height/2 - (i-Math.floor(height/40))*20 + offset_y);
        this.mGraph.mPen.setSize(1.5);
        this.mGraph.drawLine(0 + offset_x, height/2 + offset_y, width + offset_x, height/2 + offset_y);
    }

    this.drawBorder = function(size, x, y, width, height) {
        this.mGraph.mPen.setSize(size);
        this.mGraph.mPen.setColor(this.getColorDef(0,0,0));
        this.mGraph.mBrush.setColor(this.getColorDef(255,255,255,0.00001));
        this.mGraph.drawRect(x, y, width, height);
    }

    this.formatVal = function(_val) {
        return (Math.floor(_val*100+0.5)/100).toString().substr(0,6);
    }

    this.drawVals = function(offset_x, offset_y, width, height, koef, coordy) {
        this.mGraph.mPen.setColor(this.getColorDef(0,0,0));
        this.mGraph.mPen.setSize(2);
        var oldFont = this.mGraph.mFont;
        this.mGraph.mFont = this.getFontDef(null, '10px', null);
        for (var i=0; i < height/20; i++) {
            this.mGraph.drawString(this.formatVal(coordy + 20/koef*(i-Math.floor(height/40))), width + 8 + offset_x, height/2 - (i-Math.floor(height/40))*20 - 8 + offset_y);
            this.mGraph.drawLine(width + offset_x, height/2 - (i-Math.floor(height/40))*20 + offset_y, width + offset_x + 4, height/2 - (i-Math.floor(height/40))*20 + offset_y);
        }
        this.mGraph.mFont = oldFont;
    }

    this.scaleLog = function(val) {
        var ret = 1;
        while (Math.abs(val) > 1) {
            ret *= 10;
            val /= 10;
        }
        if (ret > 1) {
            ret /= 10;
            val *= 10;
        }
        return ret;
    }

    this.findUMin = function (arr) {
        var min;
        var k = 0;
        for (; k < arr.length; ++k) {
            if (!isNaN(arr[k])) {
                min = Math.abs(arr[k++]);
                break;
            }
        }
        for (var i = k; i < arr.length; ++i)
            if (!isNaN(arr[i]) && (isNaN(min) || Math.abs(arr[i]) < min)) min = Math.abs(arr[i]);
        return min;
    }

    this.findUMax = function (arr) {
        var max;
        var k = 0;
        for (; k < arr.length; ++k) {
            if (!isNaN(arr[k])) {
                max = Math.abs(arr[k++]);
                break;
            }
        }
        for (var i = k; i < arr.length; ++i)
            if (!isNaN(arr[i]) && (isNaN(max) || Math.abs(arr[i]) > max)) max = Math.abs(arr[i]);
        return max;
    }

    this.findMin = function (arr) {
        var min;
        var k = 0;
        for (; k < arr.length; ++k) {
            if (!isNaN(arr[k])) {
                min = arr[k++];
                break;
            }
        }
        for (var i = k; i < arr.length; ++i)
            if (!isNaN(arr[i]) && (isNaN(min) || arr[i] < min)) min = arr[i];
        return min;
    }

    this.findMax = function (arr) {
        var max;
        var k = 0;
        for (; k < arr.length; ++k) {
            if (!isNaN(arr[k])) {
                max = arr[k++];
                break;
            }
        }
        for (var i = k; i < arr.length; ++i)
            if (!isNaN(arr[i]) && (isNaN(max) || arr[i] > max)) max = arr[i];
        return max;
    }

    this.plotScales = function(data, width, height, plot_data_fields, plot_filtered_fields)
    {
        var global_min_vals = [];
        var min_cnt = 0;
        for (var zmin_data = 0; zmin_data < data.data_fields_count; ++zmin_data)
            if (this.contains(plot_data_fields, data.data_fields[zmin_data]))
                global_min_vals[min_cnt++] = this.findUMin(data.data[data.data_fields[zmin_data]]);
        for (var zmin_filtered = 0; zmin_filtered < data.filtered_fields_count; ++zmin_filtered)
            if (this.contains(plot_filtered_fields, data.filtered_fields[zmin_filtered]))
                global_min_vals[min_cnt++] = this.findUMin(data.filtered[data.filtered_fields[zmin_filtered]]);

        var global_max_vals = [];
        var max_cnt = 0;
        for (var zmax_data = 0; zmax_data < data.data_fields_count; ++zmax_data)
            if (this.contains(plot_data_fields, data.data_fields[zmax_data]))
                global_max_vals[max_cnt++] = this.findUMax(data.data[data.data_fields[zmax_data]]);
        for (var zmax_filtered = 0; zmax_filtered < data.filtered_fields_count; ++zmax_filtered)
            if (this.contains(plot_filtered_fields, data.filtered_fields[zmax_filtered]))
                global_max_vals[max_cnt++] = this.findUMax(data.filtered[data.filtered_fields[zmax_filtered]]);

        var glob_min = this.findUMin(global_min_vals) || 0;
        var glob_max = this.findUMax(global_max_vals) || 0;

        var scale_log_min = this.scaleLog(glob_min);
        var scale_log_max = this.scaleLog(glob_max);

        var scale_log_abs = this.findUMax([scale_log_min, scale_log_max]);

        if (scale_log_abs > 100) scale_log_abs /= 10;
        var scale_glob_min = scale_log_abs*(Math.floor(glob_min/scale_log_abs));
        var scale_glob_max = scale_log_abs*(1 + Math.floor(glob_max/scale_log_abs));

        var middle_val = Math.floor((scale_glob_max + scale_glob_min)/2 + 0.5);
        var div_val = Math.floor((scale_glob_max - scale_glob_min)/2 + 0.5);

        var koef = div_val ? height/Math.abs(div_val)/2.25 : 1;
        return [middle_val, koef];
    }

    this.drawBackground = function(arr, offset_x, offset_y, width, height) {
        this.mGraph.mPen.setSize(this.mGraphTraits.quantity_line_size);
        this.mGraph.mPen.setColor(this.getColorDef(0,0,0,0.375));
        this.mGraph.mBrush.setColor(this.getColorDef(0,0,0,0.25));

        var cur_max = this.findUMax(arr);
        if (cur_max) for (var i=0; i < arr.length; ++i)
            this.mGraph.drawRect(width - (i+1)*this.mGraphTraits.points_per_candle - this.mGraphTraits.data_delta - Math.floor(this.mGraphTraits.bar_width/2 - this.mGraphTraits.quantity_line_size) - 0.5 + offset_x,
                                 (cur_max/2 < Math.abs(arr[i])) ? height/2 - Math.floor(height*Math.abs(cur_max/2 - Math.abs(arr[i]))/cur_max) + offset_y - 0.5 : height/2 + offset_y + 0.5,
                                 this.mGraphTraits.bar_width, Math.floor(height*Math.abs(cur_max/2 - Math.abs(arr[i]))/cur_max));
    }

    this.drawRectWithShadow = function(color, x, y, width, height) {
        this.mGraph.mBrush.color.scale(1,1,1,0.1);
        this.mGraph.mPen.setColor(this.getColorDef(0,0,0,0.1));
        this.mGraph.drawRect(x + 0.5*this.mGraph.mPen.size, y + this.mGraph.mPen.size, width, height);
        this.mGraph.mBrush.color.scale(1,1,1,0.5);
        this.mGraph.mPen.setColor(this.getColorDef(0,0,0,0.05));
        this.mGraph.drawRect(x + this.mGraph.mPen.size, y + 1.5*this.mGraph.mPen.size, width, height);
        this.mGraph.mBrush.color.scale(1,1,1,20);
        this.mGraph.mPen.setColor(color);
        this.mGraph.drawRect(x, y, width, height);
    }

    this.drawLineWithShadow = function(color, xstart, ystart, xend, yend) {
        this.mGraph.mPen.setColor(this.getColorDef(0,0,0,0.05));
        this.mGraph.drawLine(xstart + this.mGraph.mPen.size, ystart + 1.5*this.mGraph.mPen.size, xend + this.mGraph.mPen.size, yend + 1.5*this.mGraph.mPen.size);
        this.mGraph.mPen.setColor(this.getColorDef(0,0,0,0.1));
        this.mGraph.drawLine(xstart + 0.5*this.mGraph.mPen.size, ystart + this.mGraph.mPen.size, xend + 0.5*this.mGraph.mPen.size, yend + this.mGraph.mPen.size);
        this.mGraph.mPen.setColor(color);
        this.mGraph.drawLine(xstart, ystart, xend, yend);
    }

    this.drawCandle = function(offset_x, offset_y, width, height, pos, koef, vshift, coordy, type, candle) {
        var open = candle[0];
        var min = candle[1];
        var max = candle[2];
        var close = candle[3];

        this.mGraph.mBrush.setColor((type == 1) || (type == 0 && open < close) ? this.getColorDef(255,255,255) : this.getColorDef(0,0,0));

        var dr_min = Math.min(open, close);
        var dr_size = Math.abs(close - open);

        this.mGraph.mPen.setSize(this.mGraphTraits.candle_tail_width);
        var delta_tail = Math.floor(this.mGraphTraits.candle_tail_width + 0.5)/2 - this.mGraphTraits.candle_tail_shift;
        var delta1_y = koef*(close - coordy);
        var delta2_y = koef*(min - coordy) - 0.5;
        var delta3_y = koef*(open - coordy);
        var delta4_y = koef*(max - coordy) + 0.5;
        if (open < close) {
            delta1_y = koef*(open - coordy);
            delta3_y = koef*(close - coordy);
        }

        if (this.mGraphTraits.points_per_candle <= 2) {
            var delta_max_y = this.findMax([delta1_y, delta2_y + 0.5, delta3_y, delta4_y - 0.5]);
            var delta_min_y = this.findMin([delta1_y, delta2_y + 0.5, delta3_y, delta4_y - 0.5]);
            this.drawLineWithShadow(this.mGraph.mPen.color,
                     delta_tail + Math.floor(width - pos*this.mGraphTraits.points_per_candle + offset_x) - this.mGraphTraits.data_delta,
                     vshift - Math.floor(delta_max_y) + offset_y,
                     delta_tail + Math.floor(width - pos*this.mGraphTraits.points_per_candle + offset_x) - this.mGraphTraits.data_delta,
                     vshift - Math.floor(delta_min_y) + offset_y);

            if (this.mGraphTraits.points_per_candle == 2 && this.contains(this.data.options, 'joined_candles')) {
                var shift_delta = (open < close ? 0.5 : -0.5);
                this.drawLineWithShadow(this.mGraph.mPen.color,
                         delta_tail + Math.floor(width - pos*this.mGraphTraits.points_per_candle + offset_x) - this.mGraphTraits.data_delta,
                         vshift - Math.floor(koef*(close - coordy)) + shift_delta + offset_y,
                         delta_tail + Math.floor(width - pos*this.mGraphTraits.points_per_candle + offset_x) + 1.5 - this.mGraphTraits.data_delta,
                         vshift - Math.floor(koef*(close - coordy)) + shift_delta + offset_y);
                this.drawLineWithShadow(this.mGraph.mPen.color,
                         delta_tail + Math.floor(width - pos*this.mGraphTraits.points_per_candle + offset_x) - this.mGraphTraits.data_delta,
                         vshift - Math.floor(koef*(open - coordy)) + shift_delta + offset_y,
                         delta_tail + Math.floor(width - pos*this.mGraphTraits.points_per_candle + offset_x) - 1.5 - this.mGraphTraits.data_delta,
                         vshift - Math.floor(koef*(open - coordy)) + shift_delta + offset_y);
            }

            return;
        } 

        this.drawLineWithShadow(this.mGraph.mPen.color,
                 delta_tail + Math.floor(width - pos*this.mGraphTraits.points_per_candle + offset_x) - this.mGraphTraits.data_delta,
                 vshift - delta1_y + offset_y,
                 delta_tail + Math.floor(width - pos*this.mGraphTraits.points_per_candle + offset_x) - this.mGraphTraits.data_delta,
                 vshift - delta2_y + offset_y);
        this.drawLineWithShadow(this.mGraph.mPen.color,
                 delta_tail + Math.floor(width - pos*this.mGraphTraits.points_per_candle + offset_x) - this.mGraphTraits.data_delta,
                 vshift - delta3_y + offset_y,
                 delta_tail + Math.floor(width - pos*this.mGraphTraits.points_per_candle + offset_x) - this.mGraphTraits.data_delta,
                 vshift - delta4_y + offset_y);

        this.mGraph.mPen.setSize(this.mGraphTraits.candle_line_size);
        var delta_line = Math.floor(this.mGraphTraits.candle_line_size + 0.5)/2 - this.mGraphTraits.candle_bar_shift;
        this.drawRectWithShadow(this.getColorDef(0,0,0),
                             delta_line - this.mGraphTraits.data_delta + Math.floor(width - pos*this.mGraphTraits.points_per_candle - Math.floor(this.mGraphTraits.bar_width/2) + offset_x),
                             delta_line + Math.floor(vshift - koef*(dr_min - coordy + dr_size) + offset_y),
                             this.mGraphTraits.bar_width,
                             Math.floor(0.5 + koef*dr_size));
    }

    this.drawXCoord = function(data, pos, offset_x, offset_y, width, height) {
        this.mGraph.drawString(data[pos - 1].substr(0,20), width  - this.mGraphTraits.data_delta - pos*this.mGraphTraits.points_per_candle - 1 + offset_x, height + offset_y + 4);
        this.mGraph.drawLine(width - this.mGraphTraits.data_delta - pos*this.mGraphTraits.points_per_candle + offset_x, height + offset_y + 5, width - this.mGraphTraits.data_delta - pos*this.mGraphTraits.points_per_candle + offset_x, height + offset_y + 1);
    }

    this.drawX = function(data, offset_x, offset_y, width, height) {
        this.mGraph.mPen.setSize(2);
        this.mGraph.mFont = this.getFontDef(null, '10px', null);

        this.drawXCoord(data, data.length, offset_x, offset_y, width, height);
        this.drawXCoord(data, 1, offset_x, offset_y, width, height);
        if (data.length > 4*this.mGraphTraits.candles_amount/5) {
            this.drawXCoord(data, Math.floor(2*(data.length+1)/3), offset_x, offset_y, width, height);
            this.drawXCoord(data, Math.floor((data.length+1)/3), offset_x, offset_y, width, height);
        } else if (data.length > 3*this.mGraphTraits.candles_amount/5) {
            this.drawXCoord(data, Math.floor((data.length+1)/2), offset_x, offset_y, width, height);
        }
    }

    this.drawLines = function(offset_x, offset_y, width, height, line, color, koef, coordy, vshift, delta) {
        this.mGraph.mPen.color.fromString(color);
        this.mGraph.mBrush.color.fromString(color);
        for (var i=0; i < line.length; ++i) {
            if (i > 0 && !isNaN(line[i]) && !isNaN(line[i-1])) {
                this.mGraph.drawLine(width - (i+1)*delta + offset_x,
                                     vshift - koef*(line[i] - coordy) + offset_y -1 + Math.floor(this.mGraphTraits.filtered_dot_size/2),
                                     width - i*delta + offset_x,
                                     vshift - koef*(line[i-1] - coordy) + offset_y -1 + Math.floor(this.mGraphTraits.filtered_dot_size/2));
            }
            if (!isNaN(line[i])) {
                this.mGraph.drawRect(width + this.mGraphTraits.points_x_shift - (i+1)*delta - this.mGraphTraits.filtered_dot_size/2 + offset_x,
                                     vshift - koef*(line[i] - coordy) - 0*this.mGraphTraits.filtered_dot_size - 1.5 + offset_y,
                                     this.mGraphTraits.filtered_dot_size,
                                     this.mGraphTraits.filtered_dot_size);
            }
        }
    }

    this.drawLinesWithShadow = function(offset_x, offset_y, width, height, line, color, koef, coordy, vshift, delta) {
        this.drawLines(offset_x + this.mGraph.mPen.size, offset_y + 1.5*this.mGraph.mPen.size, width, height, line, 'rgba(0,0,0,0.05)', koef, coordy, vshift, delta);
        this.drawLines(offset_x + 0.5*this.mGraph.mPen.size, offset_y + this.mGraph.mPen.size, width, height, line, 'rgba(0,0,0,0.1)', koef, coordy, vshift, delta);
        this.drawLines(offset_x, offset_y, width, height, line, color, koef, coordy, vshift, delta);
    }

    this.drawFilteredAndLegend = function(data, offset_x, offset_y, width, height, koef, middle_val)
    {
        var n = 0;
        for (var m = 0; m < data.filtered_fields_count; ++m)
            if (this.contains(data.visible, data.filtered_fields[m])) ++n; 

        this.mGraph.mPen.setSize(1);
        this.mGraph.mBrush.setColor(this.getColorDef(255,255,255));
        this.drawRectWithShadow(this.getColorDef(0,0,0), width + 60 + offset_x - 0.5, height/2 + offset_y - 30*((n-1)/2) - 15 - 0.5, 150, 30 * n);

        this.mGraph.mPen.setSize(this.mGraphTraits.filtered_line_size);
        this.mGraph.mFont = this.getFontDef(null, '12px', null);

        var dummy = [0, 0, 0];
        var k = 0;
        for (var j = 0; j < data.filtered_fields_count; ++j) {
            if (this.contains(data.visible, data.filtered_fields[j])) {
                this.drawLinesWithShadow(offset_x  - this.mGraphTraits.data_delta, offset_y, width, height, data.filtered[data.filtered_fields[j]], this.misc.line_colors[k % this.misc.line_colors.length], koef, middle_val, height/2, this.mGraphTraits.points_per_candle);
                this.drawLinesWithShadow(width + 75 + offset_x, height/2 + offset_y - 0.5 - 30*((n-1)/2 - k), 30, 30, dummy, this.misc.line_colors[k % this.misc.line_colors.length], 1, 0, 0, 10);
                this.mGraph.drawString(data.filtered_fields[j].substring(0,13), width + 110 + offset_x, height/2 + offset_y - 30*((n-1)/2 - k) - 10);
                ++k;
            }
        }
    }

    this.drawCross = function(x, y) {
        var i = this.getDataIndex(x, y);

        if (i < 0 || i >= this.mGraphTraits.amount) return;

        this.mGraph.mOverlayCanvas.save();
        this.mGraph.mOverlayPen.setSize(1);
        this.mGraph.mOverlayBrush.setColor(this.mSelectionColor);
        this.mGraph.clearRect(this.mGraphTraits.offset_x,this.mGraphTraits.offset_y,this.mGraphTraits.width,this.mGraphTraits.height, 'overlay');

        if (this.mGraphTraits.points_per_candle == 1) {
            this.mGraph.mOverlayPen.setColor(this.mBlackColor);
            this.mGraph.drawLine(this.mGraphTraits.width - this.mGraphTraits.data_delta + 1 - i*this.mGraphTraits.points_per_candle - 0.5 + this.mGraphTraits.offset_x,
                                 this.mGraphTraits.offset_y + 0.5,
                                 this.mGraphTraits.width - this.mGraphTraits.data_delta + 1 - i*this.mGraphTraits.points_per_candle - 0.5 + this.mGraphTraits.offset_x,
                                 this.mGraphTraits.offset_y + this.mGraphTraits.height - 0.5, 'overlay');
        } else {
            this.mGraph.mOverlayPen.setColor(this.mDarkSelectionColor);
            this.mGraph.drawRect(this.mGraphTraits.width - this.mGraphTraits.data_delta - (i+1)*this.mGraphTraits.points_per_candle - Math.floor(this.mGraphTraits.bar_width/2) - 0.5 + this.mGraphTraits.offset_x,
                                 this.mGraphTraits.offset_y + 0.5,
                                 this.mGraphTraits.points_per_candle, this.mGraphTraits.height - 0.5, 'overlay');
        }

        this.mGraph.mOverlayPen.setColor(this.mBlackColor);
        this.mGraph.drawLine(this.mGraphTraits.offset_x,
                             y-1.5,
                             this.mGraphTraits.offset_x + this.mGraphTraits.width,
                             y-1.5,
                             'overlay');
        this.mGraph.mOverlayCanvas.restore();
    }

    this.plotGraph = function(data) {
        var width = parseInt(container.style.width);
        var height = parseInt(container.style.height);

        this.mGraph.mGraphicCanvas.save();

        if (this.contains(data.options, 'nofiltered') || !data.filtered_fields_count)
            width -= 100;

        this.drawBorder(1, 0.5, 0.5, width - 1, height - 1);
        this.drawInfo(data.info.substring(0,60), width/2 - 5*data.info.substring(0,60).length, 6);

        if (this.contains(data.options, 'nofiltered') || !data.filtered_fields_count)
            width -= 130;
        else
            width -= 220;

        height -= 55;

        var offset_x = 5;
        var offset_y = 35;

        this.mGraphTraits.width = width;
        this.mGraphTraits.height = height;
        this.mGraphTraits.offset_x = offset_x;
        this.mGraphTraits.offset_y = offset_y;
        this.mGraphTraits.amount = data.data[data.data_fields[0]].length;

        if (!this.contains(data.options, 'noquantity')) {
            this.drawBackground(data.data['quantity'], offset_x, offset_y, width, height);
        }

        var plot_scales = this.plotScales(data, width, height
                                        , this.contains(data.options, 'nocandles') ? [] : ['open_val', 'min_val', 'max_val', 'close_val']
                                        , this.contains(data.options, 'nofiltered') ? [] : data.visible);
        var middle_val = plot_scales[0];
        var koef = plot_scales[1];

        this.drawBorder(3, offset_x + 0.5, offset_y + 0.5, width, height);
        this.drawGrid(offset_x, offset_y, width, height);

        if (!this.contains(data.options, 'nocandles'))
            for (var i = 0; i < this.mGraphTraits.amount; ++i)
                this.drawCandle(offset_x, offset_y,
                                width, height,
                                i + 1, koef,
                                height/2, middle_val, 0,
                                [data.data['open_val'][i],
                                 data.data['min_val'][i],
                                 data.data['max_val'][i],
                                 data.data['close_val'][i]]);

        if (!this.contains(data.options, 'nofiltered') && data.filtered_fields_count) {
            this.drawFilteredAndLegend(data, offset_x, offset_y, width, height, koef, middle_val);
        }

        this.drawVals(offset_x, offset_y, width, height, koef, middle_val);

        this.drawX(data.data['timestamp'], offset_x, offset_y, width, height);

        this.mGraph.mGraphicCanvas.restore();
    }

    this.plotGraph(this.data);

    this.checkHit = function(pos) {
        return pos.X > this.mGraphTraits.offset_x
            && pos.X < this.mGraphTraits.offset_x + this.mGraphTraits.width - this.mGraphTraits.data_delta
            && pos.Y > this.mGraphTraits.offset_y
            && pos.Y < this.mGraphTraits.offset_y + this.mGraphTraits.height;
    }

    this.getDataIndex = function(x, y) {
        var i = Math.floor((this.mGraphTraits.width - this.mGraphTraits.data_delta + this.mGraphTraits.offset_x - x)/this.mGraphTraits.points_per_candle - 0.5);
        if (i < 0) i = 0;
        return i;
    }

    this.getInfoString = function(i, delim) {
        var retString = '';

        if (i < 0 || i >= this.data.data['timestamp'].length) return retString;

        retString += 'timestamp = ' + this.data.data['timestamp'][i];
        if (!this.contains(this.data.options, 'nocandles'))
            for (var j = 0; j < this.data.data_fields_count; ++j)
                if (!this.contains(['timestamp', 'quantity'], this.data.data_fields[j]))
                    retString += delim + this.data.data_fields[j] + ' = ' + this.formatVal(this.data.data[this.data.data_fields[j]][i]);
        if (!this.contains(this.data.options, 'noquantity'))
            retString += delim + 'quantity = ' + this.formatVal(this.data.data['quantity'][i]);
        if (!this.contains(this.data.options, 'nofiltered'))
            for (var j = 0; j < this.data.filtered_fields_count; ++j)
                //if (this.contains(this.data.visible, this.data.filtered_fields[j]))
                    retString += delim + this.data.filtered_fields[j] + ' = ' + this.formatVal(this.data.filtered[this.data.filtered_fields[j]][i]);

        return retString;
    }

    this.onMouseMoveHandler = function(_this, pos, evt) {
        if (_this.checkHit(pos)) {
            var i = _this.getDataIndex(pos.X, pos.Y);

            _this.drawCross(pos.X - 1, pos.Y);
            if (i >= 0 && i < _this.mGraphTraits.amount) {
                _this.mGraph.mPopupCanvas.style.display = 'block';
                _this.mGraph.mPopupCanvas.style.left = pos.X + 20;
                _this.mGraph.mPopupCanvas.style.top = pos.Y + 10;
                _this.mGraph.mFont = _this.getFontDef(null, '10px', null);

                while (_this.mGraph.mPopupCanvas.childNodes.length)
                    _this.mGraph.mPopupCanvas.removeChild(_this.mGraph.mPopupCanvas.childNodes[0]);
                _this.misc.seegePrintTextElement(_this.mGraph.mPopupCanvas,'<div style="font-size: 12px">' + _this.getInfoString(i, '<br>\n') + '</div>',_this.browser);
            } else {
                _this.mGraph.mPopupCanvas.style.display = 'none';
            }
        } else {
            _this.mGraph.mPopupCanvas.style.display = 'none';
        }
    }
    this.onMouseOverHandler = function(_this, pos, evt) {}
    this.onMouseOutHandler = function(_this, pos, evt) {
        _this.mGraph.mPopupCanvas.style.display = 'none';
    }
    this.onMouseClickHandler = function(_this, pos, evt) {
        if (_this.checkHit(pos)) {
            var i = _this.getDataIndex(pos.X, pos.Y);
            if (i < 0 || i >= _this.data.data['timestamp'].length) return;
            alert (_this.getInfoString(i, "\n"));
        }
    }

    this.mMouseAdapter = new misc.seegeMouseAdapterDef(container, this);

    this.drawCross(this.mGraphTraits.offset_x + this.mGraphTraits.width - this.mGraphTraits.data_delta - this.mGraphTraits.points_per_candle/2, this.mGraphTraits.offset_y + this.mGraphTraits.height/2 + 1);
}

